/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// image.c - image handling TODO: add jpeg support

#include "quakedef.h"
#include "png.h"
#include "targa.h"

#define	IMAGE_MAX_DIMENSIONS	4096
byte			*image_rgba;
int				image_width;
int				image_height;

/*
=========================================================

Targa

=========================================================
*/
int fgetLittleShort (FILE *f)
{
    byte	b1, b2;

    b1 = fgetc(f);
    b2 = fgetc(f);

    return (short)(b1 + b2*256);
}

int fgetLittleLong (FILE *f)
{
    byte	b1, b2, b3, b4;

    b1 = fgetc(f);
    b2 = fgetc(f);
    b3 = fgetc(f);
    b4 = fgetc(f);

    return b1 + (b2<<8) + (b3<<16) + (b4<<24);
}

qboolean LoadSimpleTGA (FILE *f, int size, char *name)
{
    int			w, h, x, y, realrow, truerow, baserow, i, temp1, temp2, pixel_size, map_idx;
    int			RLE_count, RLE_flag, interleave, origin;
    qboolean	mapped, rlencoded;
    byte		*mem, *dst, r, g, b, a, j, k, l, *ColorMap;
    TargaHeader	header;

    if (!f && COM_FOpenFile(name, &f) == -1)
    {
        return false;
    }

    /* load file */
    header.id_length = fgetc (f);
    header.colormap_type = fgetc (f);
    header.image_type = fgetc (f);
    header.colormap_index = fgetLittleShort (f);
    header.colormap_length = fgetLittleShort (f);
    header.colormap_size = fgetc (f);
    header.x_origin = fgetLittleShort (f);
    header.y_origin = fgetLittleShort (f);
    header.width = fgetLittleShort (f);
    header.height = fgetLittleShort (f);
    header.pixel_size = fgetc (f);
    header.attributes = fgetc (f);

    if (header.width > IMAGE_MAX_DIMENSIONS || header.height > IMAGE_MAX_DIMENSIONS)
    {
        Con_Printf ("TGA image %s exceeds maximum supported dimensions\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }

    if (header.id_length != 0)
    {
        fseek (f, header.id_length, SEEK_CUR);
    }

    /* validate TGA type */
    switch (header.image_type)
    {
    case TGA_Map:
    case TGA_RGB:
    case TGA_Mono:
    case TGA_RLEMap:
    case TGA_RLERGB:
    case TGA_RLEMono:
        break;

    default:
        Con_Printf ("Unsupported TGA image %s: Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }

    /* validate color depth */
    switch (header.pixel_size)
    {
    case 8:
    case 15:
    case 16:
    case 24:
    case 32:
        break;

    default:
        Con_Printf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit images (with colormaps) supported\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }
    r = g = b = a = l = 0;

    /* if required, read the color map information. */
    ColorMap = NULL;
    mapped = (header.image_type == TGA_Map || header.image_type == TGA_RLEMap) && header.colormap_type == 1;

    if (mapped)
    {
        /* validate colormap size */
        switch (header.colormap_size)
        {
        case 8:
        case 15:
        case 16:
        case 32:
        case 24:
            break;

        default:
            Con_Printf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit colormaps supported\n", COM_SkipPath(name));
            fclose (f);
            return false;
        }
        temp1 = header.colormap_index;
        temp2 = header.colormap_length;

        if ((temp1 + temp2 + 1) >= TGA_MAXCOLORS)
        {
            fclose (f);
            return false;
        }
        ColorMap = malloc (TGA_MAXCOLORS * size);
        map_idx = 0;

        for (i = temp1 ; i < temp1 + temp2 ; ++i, map_idx += 4)
        {
            /* read appropriate number of bytes, break into rgb & put in map. */
            switch (header.colormap_size)
            {
            case 8:	/* grey scale, read and triplicate. */
                r = g = b = getc (f);
                a = 255;
                break;

            case 15:	/* 5 bits each of red green and blue. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = 255;
                break;

            case 16:	/* 5 bits each of red green and blue, 1 alpha bit. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = (k & 0x80) ? 255 : 0;
                break;

            case 24:	/* 8 bits each of blue, green and red. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = 255;
                l = 0;
                break;

            case 32:	/* 8 bits each of blue, green, red and alpha. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = getc (f);
                l = 0;
                break;
            }
            ColorMap[map_idx+0] = r;
            ColorMap[map_idx+1] = g;
            ColorMap[map_idx+2] = b;
            ColorMap[map_idx+3] = a;
        }
    }

    /* check run-length encoding. */
    rlencoded = (header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono);
    RLE_count = RLE_flag = 0;

    image_width = w = header.width;
    image_height = h = header.height;
    image_rgba = mem = calloc (w * h * size, 1);

    /* read the Targa file body and convert to portable format. */
    pixel_size = header.pixel_size;
    origin = (header.attributes & 0x20) >> 5;
    interleave = (header.attributes & 0xC0) >> 6;
    truerow = baserow = 0;

    for (y=0 ; y<h ; y++)
    {
        realrow = truerow;

        if (origin == TGA_O_UPPER)
        {
            realrow = h - realrow - 1;
        }
        dst = mem + realrow * w * size;

        for (x=0 ; x<w ; x++)
        {
            /* check if run length encoded. */
            if (rlencoded)
            {
                if (!RLE_count)
                {
                    /* have to restart run. */
                    i = getc (f);
                    RLE_flag = (i & 0x80);
                    if (!RLE_flag)	// stream of unencoded pixels
                        RLE_count = i + 1;
                    else		// single pixel replicated
                        RLE_count = i - 127;
                    /* decrement count & get pixel. */
                    --RLE_count;
                }
                else
                {
                    /* have already read count & (at least) first pixel. */
                    --RLE_count;
                    if (RLE_flag)
                        /* replicated pixels. */
                        goto PixEncode;
                }
            }

            /* read appropriate number of bytes, break into RGB. */
            switch (pixel_size)
            {
            case 8:	/* grey scale, read and triplicate. */
                r = g = b = l = getc (f);
                a = 255;
                break;

            case 15:	/* 5 bits each of red green and blue. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = 255;
                break;

            case 16:	/* 5 bits each of red green and blue, 1 alpha bit. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = (k & 0x80) ? 255 : 0;
                break;

            case 24:	/* 8 bits each of blue, green and red. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = 255;
                l = 0;
                break;

            case 32:	/* 8 bits each of blue, green, red and alpha. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = getc (f);
                l = 0;
                break;

            default:
                Con_Printf ("Malformed TGA image %s: Illegal pixel_size '%d'\n", COM_SkipPath(name), pixel_size);
                fclose (f);
                free (mem);
                if (mapped)
                {
                    free (ColorMap);
                }
                return false;
            }

PixEncode:
            if (mapped)
            {
                map_idx = l * size;
                *dst++ = ColorMap[map_idx+0];
                *dst++ = ColorMap[map_idx+1];
                *dst++ = ColorMap[map_idx+2];
                *dst++ = ColorMap[map_idx+3];
            }
            else
            {
                *dst++ = r;
                *dst++ = g;
                *dst++ = b;
                *dst++ = a;
            }
        }

        if (interleave == TGA_IL_Four)
        {
            truerow += 4;
        }
        else if (interleave == TGA_IL_Two)
        {
            truerow += 2;
        }
        else
        {
            truerow++;
        }

        if (truerow >= h)
        {
            truerow = ++baserow;
        }
    }

    if (mapped)
    {
        free (ColorMap);
    }
    fclose (f);
    return true;
}

qboolean LoadTGA (FILE *f, byte *mem, int *width, int *height, int size, char *name)
{
    int			w, h, x, y, realrow, truerow, baserow, i, temp1, temp2, pixel_size, map_idx;
    int			RLE_count, RLE_flag, interleave, origin;
    qboolean	mapped, rlencoded;
    byte		*dst, r, g, b, a, j, k, l, *ColorMap;
    TargaHeader	header;

    if (!f && COM_FOpenFile(name, &f) == -1)
    {
        return false;
    }

    /* load file */
    header.id_length = fgetc (f);
    header.colormap_type = fgetc (f);
    header.image_type = fgetc (f);
    header.colormap_index = fgetLittleShort (f);
    header.colormap_length = fgetLittleShort (f);
    header.colormap_size = fgetc (f);
    header.x_origin = fgetLittleShort (f);
    header.y_origin = fgetLittleShort (f);
    header.width = fgetLittleShort (f);
    header.height = fgetLittleShort (f);
    header.pixel_size = fgetc (f);
    header.attributes = fgetc (f);

    if (header.width > IMAGE_MAX_DIMENSIONS || header.height > IMAGE_MAX_DIMENSIONS)
    {
        Con_Printf ("TGA image %s exceeds maximum supported dimensions\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }

    if (header.id_length != 0)
    {
        fseek (f, header.id_length, SEEK_CUR);
    }

    /* validate TGA type */
    switch (header.image_type)
    {
    case TGA_Map:
    case TGA_RGB:
    case TGA_Mono:
    case TGA_RLEMap:
    case TGA_RLERGB:
    case TGA_RLEMono:
        break;

    default:
        Con_Printf ("Unsupported TGA image %s: Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }

    /* validate color depth */
    switch (header.pixel_size)
    {
    case 8:
    case 15:
    case 16:
    case 24:
    case 32:
        break;

    default:
        Con_Printf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit images (with colormaps) supported\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }
    r = g = b = a = l = 0;

    /* if required, read the color map information. */
    ColorMap = NULL;
    mapped = (header.image_type == TGA_Map || header.image_type == TGA_RLEMap) && header.colormap_type == 1;

    if (mapped)
    {
        /* validate colormap size */
        switch (header.colormap_size)
        {
        case 8:
        case 15:
        case 16:
        case 32:
        case 24:
            break;

        default:
            Con_Printf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit colormaps supported\n", COM_SkipPath(name));
            fclose (f);
            return false;
        }
        temp1 = header.colormap_index;
        temp2 = header.colormap_length;

        if ((temp1 + temp2 + 1) >= TGA_MAXCOLORS)
        {
            fclose (f);
            return false;
        }
        ColorMap = malloc (TGA_MAXCOLORS * size);
        map_idx = 0;

        for (i = temp1 ; i < temp1 + temp2 ; ++i, map_idx += 4)
        {
            /* read appropriate number of bytes, break into rgb & put in map. */
            switch (header.colormap_size)
            {
            case 8:	/* grey scale, read and triplicate. */
                r = g = b = getc (f);
                a = 255;
                break;

            case 15:	/* 5 bits each of red green and blue. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = 255;
                break;

            case 16:	/* 5 bits each of red green and blue, 1 alpha bit. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = (k & 0x80) ? 255 : 0;
                break;

            case 24:	/* 8 bits each of blue, green and red. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = 255;
                l = 0;
                break;

            case 32:	/* 8 bits each of blue, green, red and alpha. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = getc (f);
                l = 0;
                break;
            }
            ColorMap[map_idx+0] = r;
            ColorMap[map_idx+1] = g;
            ColorMap[map_idx+2] = b;
            ColorMap[map_idx+3] = a;
        }
    }

    /* check run-length encoding. */
    rlencoded = (header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono);
    RLE_count = RLE_flag = 0;

    *width = w = header.width;
    *height = h = header.height;

    /* read the Targa file body and convert to portable format. */
    pixel_size = header.pixel_size;
    origin = (header.attributes & 0x20) >> 5;
    interleave = (header.attributes & 0xC0) >> 6;
    truerow = baserow = 0;

    for (y=0 ; y<h ; y++)
    {
        realrow = truerow;

        if (origin == TGA_O_UPPER)
        {
            realrow = h - realrow - 1;
        }
        dst = mem + realrow * w * size;

        for (x=0 ; x<w ; x++)
        {
            /* check if run length encoded. */
            if (rlencoded)
            {
                if (!RLE_count)
                {
                    /* have to restart run. */
                    i = getc (f);
                    RLE_flag = (i & 0x80);
                    if (!RLE_flag)	// stream of unencoded pixels
                        RLE_count = i + 1;
                    else		// single pixel replicated
                        RLE_count = i - 127;
                    /* decrement count & get pixel. */
                    --RLE_count;
                }
                else
                {
                    /* have already read count & (at least) first pixel. */
                    --RLE_count;
                    if (RLE_flag)
                        /* replicated pixels. */
                        goto PixEncode;
                }
            }

            /* read appropriate number of bytes, break into RGB. */
            switch (pixel_size)
            {
            case 8:	/* grey scale, read and triplicate. */
                r = g = b = l = getc (f);
                a = 255;
                break;

            case 15:	/* 5 bits each of red green and blue. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = 255;
                break;

            case 16:	/* 5 bits each of red green and blue, 1 alpha bit. */
                /* watch byte order. */
                j = getc (f);
                k = getc (f);
                l = ((unsigned int)k << 8) + j;
                r = (byte)(((k & 0x7C) >> 2) << 3);
                g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3);
                b = (byte)((j & 0x1F) << 3);
                a = (k & 0x80) ? 255 : 0;
                break;

            case 24:	/* 8 bits each of blue, green and red. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = 255;
                l = 0;
                break;

            case 32:	/* 8 bits each of blue, green, red and alpha. */
                b = getc (f);
                g = getc (f);
                r = getc (f);
                a = getc (f);
                l = 0;
                break;

            default:
                Con_Printf ("Malformed TGA image %s: Illegal pixel_size '%d'\n", COM_SkipPath(name), pixel_size);
                fclose (f);
                free (mem);
                if (mapped)
                {
                    free (ColorMap);
                }
                return false;
            }

PixEncode:
            if (mapped)
            {
                map_idx = l * size;
                *dst++ = ColorMap[map_idx+0];
                *dst++ = ColorMap[map_idx+1];
                *dst++ = ColorMap[map_idx+2];
                *dst++ = ColorMap[map_idx+3];
            }
            else
            {
                *dst++ = r;
                *dst++ = g;
                *dst++ = b;
                *dst++ = a;
            }
        }

        if (interleave == TGA_IL_Four)
        {
            truerow += 4;
        }
        else if (interleave == TGA_IL_Two)
        {
            truerow += 2;
        }
        else
        {
            truerow++;
        }

        if (truerow >= h)
        {
            truerow = ++baserow;
        }
    }

    if (mapped)
    {
        free (ColorMap);
    }
    fclose (f);
    return true;
}

/*
=========================================================

PNG

=========================================================
*/
static void png_io_user_read_data (png_structp png_ptr, png_bytep data, png_size_t length)
{
    FILE	*f = (FILE *)png_get_io_ptr(png_ptr);

    fread (data, 1, length, f);
}

qboolean LoadSimplePNG (FILE *f, int size, char *name)
{
    int				y, pixwidth, pixheight, bitdepth, colortype;
    byte			header[8], **rowpointers, *mem;
    png_structp		png_ptr;
    png_infop		pnginfo;

    if (!f && COM_FOpenFile(name, &f) == -1)
    {
        return false;
    }
    fread (header, 1, 8, f);

    if (png_sig_cmp(header, 0, 8) != 0)
    {
        Con_Printf ("Invalid PNG image %s\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }

    if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
    {
        fclose (f);
        return false;
    }

    if (!(pnginfo = png_create_info_struct(png_ptr)))
    {
        png_destroy_read_struct (&png_ptr, &pnginfo, 0);
        fclose (f);
        return false;
    }

    if (setjmp(png_jmpbuf(png_ptr)))
    {
        png_destroy_read_struct (&png_ptr, &pnginfo, 0);
        fclose (f);
        return false;
    }
    png_set_read_fn (png_ptr, f, png_io_user_read_data);
    png_set_sig_bytes (png_ptr, 8);
    png_read_info (png_ptr, pnginfo);
    png_get_IHDR (png_ptr, pnginfo, (png_uint_32 *)&pixwidth, (png_uint_32 *)&pixheight, &bitdepth, &colortype, 0, 0, 0);

    if (pixwidth > IMAGE_MAX_DIMENSIONS || pixheight > IMAGE_MAX_DIMENSIONS)
    {
        Con_Printf ("PNG image %s exceeds maximum supported dimensions\n", COM_SkipPath(name));
        png_destroy_read_struct (&png_ptr, &pnginfo, 0);
        fclose (f);
        return false;
    }

    if (bitdepth == 16)
    {
        png_set_strip_16(png_ptr);
    }

    if (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8)
    {
        png_set_expand_gray_1_2_4_to_8 (png_ptr);
    }
    png_set_gamma(png_ptr, 1.0, 1.0);

    // YES IT IS INTENDED!
    if (size == 4)
    {
        if (colortype & PNG_COLOR_MASK_PALETTE)
        {
            png_set_palette_to_rgb(png_ptr);
        }

        if (png_get_valid(png_ptr, pnginfo, PNG_INFO_tRNS))
        {
            png_set_tRNS_to_alpha(png_ptr);
        }

        if ((colortype & PNG_COLOR_MASK_COLOR) == 0)
        {
            png_set_gray_to_rgb(png_ptr);
        }

        if ((colortype & PNG_COLOR_MASK_ALPHA) == 0)
        {
            png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
        }
    }
    else
    {
        if (colortype & PNG_COLOR_MASK_ALPHA)
        {
            png_set_strip_alpha(png_ptr);
        }

        if (colortype & PNG_COLOR_MASK_PALETTE)
        {
            png_set_palette_to_rgb(png_ptr);

        }

        if (colortype & PNG_COLOR_MASK_COLOR)
        {
            png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
        }
    }

    // Get data here
    image_rgba = mem = malloc (pixheight * pixwidth * size);
    rowpointers = malloc (pixheight * sizeof(*rowpointers));

    for (y = 0 ; y < pixheight ; y++)
    {
        rowpointers[y] = mem + size * pixwidth * y;
    }

    // fill in properties
    image_width = pixwidth;
    image_height = pixheight;

    png_read_image (png_ptr, rowpointers);
    png_read_end (png_ptr, 0);
    png_destroy_read_struct (&png_ptr, &pnginfo, 0);
    free (rowpointers);
    fclose (f);
    return true;
}

qboolean LoadPNG (FILE *f, byte *mem, int *width, int *height, int size, char *name)
{
    int				y, pixwidth, pixheight, bitdepth, colortype;
    byte			header[8], **rowpointers;
    png_structp		png_ptr;
    png_infop		pnginfo;

    if (!f && COM_FOpenFile(name, &f) == -1)
    {
        return false;
    }
    fread (header, 1, 8, f);

    if (png_sig_cmp(header, 0, 8) != 0)
    {
        Con_Printf ("Invalid PNG image %s\n", COM_SkipPath(name));
        fclose (f);
        return false;
    }

    if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
    {
        fclose (f);
        return false;
    }

    if (!(pnginfo = png_create_info_struct(png_ptr)))
    {
        png_destroy_read_struct (&png_ptr, &pnginfo, 0);
        fclose (f);
        return false;
    }

    if (setjmp(png_jmpbuf(png_ptr)))
    {
        png_destroy_read_struct (&png_ptr, &pnginfo, 0);
        fclose (f);
        return false;
    }
    png_set_read_fn (png_ptr, f, png_io_user_read_data);
    png_set_sig_bytes (png_ptr, 8);
    png_read_info (png_ptr, pnginfo);
    png_get_IHDR (png_ptr, pnginfo, (png_uint_32 *)&pixwidth, (png_uint_32 *)&pixheight, &bitdepth, &colortype, 0, 0, 0);

    if (pixwidth > IMAGE_MAX_DIMENSIONS || pixheight > IMAGE_MAX_DIMENSIONS)
    {
        Con_Printf ("PNG image %s exceeds maximum supported dimensions\n", COM_SkipPath(name));
        png_destroy_read_struct (&png_ptr, &pnginfo, 0);
        fclose (f);
        return false;
    }

    if (bitdepth == 16)
    {
        png_set_strip_16(png_ptr);
    }

    if (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8)
    {
        png_set_expand_gray_1_2_4_to_8 (png_ptr);
    }
    png_set_gamma(png_ptr, 1.0, 1.0);

    // YES IT IS INTENDED!
    if (size == 4)
    {
        if (colortype & PNG_COLOR_MASK_PALETTE)
        {
            png_set_palette_to_rgb(png_ptr);
        }

        if (png_get_valid(png_ptr, pnginfo, PNG_INFO_tRNS))
        {
            png_set_tRNS_to_alpha(png_ptr);
        }

        if ((colortype & PNG_COLOR_MASK_COLOR) == 0)
        {
            png_set_gray_to_rgb(png_ptr);
        }

        if ((colortype & PNG_COLOR_MASK_ALPHA) == 0)
        {
            png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
        }
    }
    else
    {
        if (colortype & PNG_COLOR_MASK_ALPHA)
        {
            png_set_strip_alpha(png_ptr);
        }

        if (colortype & PNG_COLOR_MASK_PALETTE)
        {
            png_set_palette_to_rgb(png_ptr);

        }

        if (colortype & PNG_COLOR_MASK_COLOR)
        {
            png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
        }
    }

    // We get data from elsewhere -> eg. models
    rowpointers = malloc (pixheight * sizeof(*rowpointers));

    for (y = 0 ; y < pixheight ; y++)
    {
        rowpointers[y] = mem + size * pixwidth * y;
    }

    // fill in properties
    *width = pixwidth;
    *height = pixheight;

    png_read_image (png_ptr, rowpointers);
    png_read_end (png_ptr, 0);
    png_destroy_read_struct (&png_ptr, &pnginfo, 0);
    free (rowpointers);
    fclose (f);
    return true;
}

// Proper replacement for LoadTGA and friends
qboolean Image_LoadTexture(char *filename, int size)
{
    FILE		*f;
    char		basename[MAX_OSPATH], name[MAX_OSPATH];

    // Check for *.png first, fall back to *.tga if not found...
    COM_StripExtension (filename, basename);

    va_snprintf(name, MAX_OSPATH, "%s.png", basename);

    if (COM_FOpenFile (name, &f) != -1)
    {
        return LoadSimplePNG (f, size, name);
    }
    va_snprintf(name, MAX_OSPATH, "%s.tga", basename);

    if (COM_FOpenFile (name, &f) != -1)
    {
        return LoadSimpleTGA (f, size, name);
    }
    return false;
}

qboolean Image_LoadTextureInPlace(char *filename, int size, byte *mem, int *width, int *height)
{
    FILE	*f;
    char	basename[MAX_OSPATH], name[MAX_OSPATH];

    // Check for *.png first, fall back to *.tga if not found...
    COM_StripExtension (filename, basename);

    va_snprintf(name, MAX_OSPATH, "%s.png", basename);

    if (COM_FOpenFile (name, &f) != -1)
    {
        return LoadPNG (f, mem, width, height, size, name);
    }
    va_snprintf(name, MAX_OSPATH, "%s.tga", basename);

    if (COM_FOpenFile (name, &f) != -1)
    {
        return LoadTGA (f, mem, width, height, size, name);
    }
    return false;
}

/*
==================
PENTA:
Loads the image in filename and returns the texture object
in gl texture object. (steaming pile of gank unfortunatly we have to live with it)
==================
*/
int Image_LoadShader(char *filename)
{
    int	texturemode;

    if ( gl_texcomp && ((int)gl_compress_textures.value) & 1 )
    {
        texturemode = GL_COMPRESSED_RGBA_ARB;
    }
    else
    {
        texturemode = GL_RGBA8;
    }
    GL_Bind (texture_extension_number);

    if(!Image_LoadTexture (filename, 4))
    {
        Con_Printf ("Couldn't load %s\n", filename);
        return 0;
    }
    glTexImage2D (GL_TEXTURE_2D, 0, texturemode, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_rgba);

    free (image_rgba);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    texture_extension_number++;

    return texture_extension_number-1;
}
